//
//  Lattice.h
//  phi_four
//
//  Created by Snir Gazit on 3/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#ifndef Lattice_h
#define Lattice_h

#include "../HelpLibs/ArrayMultiD.h"
#include "../HelpLibs/def.h"

using namespace std;
using namespace boost;

typedef boost::array<int, DIM> IndexType;

enum MoveDir {
	RIGHT_ARROW = 0, LEFT_ARROW = 1
};

enum CreateRemove {
	CREATE = 0, REMOVE = 1
};

enum SectorType {
	ZSECTOR = 0, GXY = 1, GISING = 2
};

typedef boost::array<int, 2> XYBondType;
typedef int IsingBondType;

// Get Sector Type
template<typename BondType>
SectorType getSectorType();
template<typename BondType>
double gFactor();
template<typename BondType>
double addRatio(int n1, int n2);
template<typename BondType>
double removeRatio(int n1, int n2);

struct Site {
	typedef ArrayMulti<Site, DIM> SiteArray;
	typedef SiteArray::iterator SiteArrayIterator;
	typedef SiteArray::IterIndex SiteArrayIndexIterator;
	typedef boost::array<XYBondType*, 2 * DIM> XYBondArray;
	typedef boost::array<IsingBondType*, 2 * DIM> IsingBondArray;
	typedef boost::array<SiteArrayIterator, 2 * DIM> NSiteArray;

	XYBondArray XYBondArray_;
	IsingBondArray IsingBondArray_;
	NSiteArray nSiteArray_;
	int XYTotalBonds_;
	int IsingTotalBonds_;
	int getTotalBonds() {
		return XYTotalBonds_ + IsingTotalBonds_;
	}
	template<class BondType>
	int& getTotalBondsType();
	template<class BondType>
	boost::array<BondType*, 2 * DIM>& getBondArray();
	template<class BondType>
	int& getTotalBondsOther();
	int getTotalBondsAll() {
		return XYTotalBonds_ + IsingTotalBonds_;
	}
	friend class serialization::access;
	template<class Archive>
	void serialize(Archive & ar, const unsigned int version) {
		ar & XYTotalBonds_;
		ar & IsingTotalBonds_;
	}
};

struct Lattice {
	// Typedefs
	// XY Bonds
	typedef boost::array<XYBondType, DIM> XYBondData;
	typedef XYBondData::iterator XYBondDataVectorIterator;
	// Ising Bonds
	typedef boost::array<IsingBondType, DIM> IsingBondData;
	typedef IsingBondData::iterator IsingBondDataVectorIterator;
	// Site Iterator
	typedef Site::SiteArrayIterator SiteIter;
	typedef Site::SiteArrayIndexIterator SiteIterIndex;
	// Bond Array
	typedef ArrayMulti<XYBondData, DIM> XYBondArray;
	typedef ArrayMulti<IsingBondData, DIM> IsingBondArray;
	typedef XYBondArray::iterator XYBondArrayIter;
	typedef IsingBondArray::iterator IsingBondArrayIter;
	// functions
	void init(IndexType const &latSize);
	void initSquare(IndexType const &latSize);
	SiteIter getIra();
	SiteIter getMasha();
	void setIra(Lattice::SiteIter &iter);
	void setMasha(Lattice::SiteIter &iter);
	SiteIter getSite(int distanceFromStart);
	boost::array<int, DIM> getSizes();
	bool isClosed() const;
	void moveIraMasha(IndexType moveToPos);
	int getCordinationNumber() const;
	int getNumOfSites() const;
	// Debug Functions
	bool testConfiguration(int &totalBonds);
	// Measure Functions
	XYBondArrayIter XYBondBegin();
	XYBondArrayIter XYBondEnd();
	IsingBondArrayIter IsingBondBegin();
	IsingBondArrayIter IsingBondEnd();
	SiteIter siteBegin();
	SiteIter siteEnd();
	SiteIterIndex siteBeginIndex();
	SiteIterIndex siteEndIndex();
	SectorType getSectorType() const;
	void setSectorType(SectorType sectorType);
	int getTotalBonds() const;
	void setToalBonds(int totalBonds);
	void dump(string fileName, bool final, ofstream *outFile);
	void read(string fileName,bool check);
	template<class Archive>
	void save(Archive & ar, const unsigned int version) const;
	template<class Archive>
	void load(Archive & ar, const unsigned int version);BOOST_SERIALIZATION_SPLIT_MEMBER()Site::SiteArray siteLattice_;
	XYBondArray XYBondLattice_;
	IsingBondArray IsingBondLattice_;
	SiteIter ira_, masha_;
	boost::array<int, DIM> latSize_;
	int cordinationNumber_;
	SectorType sectorType_;
	int totalBonds_;
};
template<class Archive>
void Lattice::save(Archive & ar, const unsigned int version) const {
	for (auto itb = XYBondLattice_.begin(); itb != XYBondLattice_.end(); itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = IsingBondLattice_.begin(); itb != IsingBondLattice_.end();
			itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = siteLattice_.begin(); itb != siteLattice_.end(); itb++)
		ar & *itb;
	int ira = ira_ - siteLattice_.begin();
	ar & ira;
	int masha = masha_ - siteLattice_.begin();
	ar & masha;
	ar & sectorType_;
	ar & totalBonds_;
}
template<class Archive>
void Lattice::load(Archive & ar, const unsigned int version) {
	for (auto itb = XYBondLattice_.begin(); itb != XYBondLattice_.end(); itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = IsingBondLattice_.begin(); itb != IsingBondLattice_.end();
			itb++)
		for (auto itd = itb->begin(); itd != itb->end(); itd++)
			ar & *itd;
	for (auto itb = siteLattice_.begin(); itb != siteLattice_.end(); itb++)
		ar & *itb;
	int ira;
	ar & ira;
	ira_ = this->siteBegin() + ira;
	int masha;
	ar & masha;
	masha_ = this->siteBegin() + masha;
	ar & sectorType_;
	ar & totalBonds_;
}
#endif
